package com.my.ddd_sample_collections.aggregate_change_tracking.repository_framework;


import lombok.Builder;
import lombok.experimental.SuperBuilder;

import java.util.*;

import static com.my.ddd_sample_collections.aggregate_change_tracking.repository_framework.DomainObjectStatus.*;

@SuperBuilder
public abstract class DomainObjectSupport implements DomainObject {

    @Builder.Default
    private DomainObjectStatus status = NEW;
    
    private final Set<String> changedFields = new HashSet<>();
    private final Map<DeletedDomainObjectKey, DomainObject> deletedChildObjects = new HashMap<>();

    private void trackSimple(String fieldName) {
        if(changedFields.contains(fieldName)){
            return;
        }else {
            changedFields.add(fieldName);
        }
    }
    
    @Override
    public void track(DomainObject parent, Object thisValue, String propertyName, Object toValue) {
        if (isNew()) {//新建对象无需跟踪变化
            return;
        }
        
        if (thisValue != null && thisValue instanceof DomainObject) {//复杂属性设为空（即：删除），或 直接换成一个新对象，需要把旧对象标记为delete
            //只对第一次设置为nll进行跟踪，如果后续设置为其他非null值。则认为是 删除然后再新增
            //考虑不同实体中属性重名的问题
            DeletedDomainObjectKey key = new DeletedDomainObjectKey(parent, propertyName);
            if (!deletedChildObjects.containsKey(key)) {
                deletedChildObjects.put(key, (DomainObject) thisValue);
                deepMarkDeleted((DomainObject) toValue);
            }
        } else if (thisValue == null && toValue != null && toValue instanceof DomainObject) {//首次设置为新对象
            //do nothing        } else if (thisValue == null && toValue != null && toValue instanceof BaseDomainObject) {//首次设置为新对象
        } else if (toValue != null && toValue instanceof Collection<?>) {//FIXME 禁止设置集合属性为新值？
            throw new UnsupportedOperationException("领域对象中，禁止设置集合属性为新对象，集合对象默认已初始化为空集合，请通过集合的方法添加或删除对象");
        } else if (toValue != null && toValue instanceof DomainObject[]) {//FIXME 禁止使用实体数组属性？
            throw new UnsupportedOperationException("领域对象中，禁止使用实体数组，请使用集合");
        } else {//简单属性（包含）
            trackSimple(propertyName);
        }
    }
    
    @Override
    public void trackRemoveItem(DomainObject parent, String propertyName, DomainObject toRemove) {
        DeletedDomainObjectKey key = new DeletedDomainObjectKey(parent, propertyName+toRemove.hashCode());
        if (!deletedChildObjects.containsKey(key)) {
            deletedChildObjects.put(key, toRemove);
            deepMarkDeleted(toRemove);
        }
    }
    
    private void deepMarkDeleted(DomainObject toDelete) {
        toDelete.markDeleted();
        //TODO 深层标记对象删除状态（如果被删除的实体还有子实体，那么它的子实体也应该被标注为删除（是否存在争议？））
    }
    
    @Override
    public boolean isTrackingField(String fieldName) {
        return changedFields.contains(fieldName);
    }
    
    @Override
    public boolean isDeleted(DomainObject parent, String entityPropertyName) {
        return deletedChildObjects.containsKey(new DeletedDomainObjectKey(parent, entityPropertyName));
    }
    
    @Override
    public void clearTracking() {
        changedFields.clear();
    }

    @Override
    public boolean isUnchanged() {
        return status == UNCHANGED;
    }

    @Override
    public Set<String> getChangedFields() {
        return this.changedFields;
    }

    /**
     * 新构建的实体对象是new状态（为了方便业务处理过程中新建实体），所以仓储加载实体结束时，调用该方法设置为正确状态。
     */
    @Override
    public void markUnchanged(){
        clearTracking();
        this.status = UNCHANGED;
    }

    @Override
    public boolean isChanged() {
        return this.status==CHANGED;
    }

    @Override
    public boolean isNew() {
        return this.status== NEW;
    }
    
    @Override
    public boolean isDeleted() {
        return this.status==DELETED;
    }
    
    @Override
    public void markDeleted() {
        this.status = DELETED;
    }
    
    public Map<DeletedDomainObjectKey, DomainObject> getDeleteChildObjects() {
        //实现返回被删除的实体清单
        return deletedChildObjects;
    }

}
